package com.kancy.streamplus.channel;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.json.PackageVersion;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import com.kancy.streamplus.config.DynamicInterfaceBeanResolver;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.lang.reflect.ParameterizedType;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author kancy
 * @date 2019/12/20 13:47
 **/
abstract class AbstractPollingMessageListener {

    private static final Pattern LINE_PATTERN = Pattern.compile("-(\\w)");

    private static final JacksonObjectMapper JACKSON_OBJECT_MAPPER = new JacksonObjectMapper();

    private final DynamicInterfaceBeanResolver dynamicInterfaceBeanResolver;

    private final List<PollingMessageExceptionHandler> pollingMessageExceptionHandlers;

    public AbstractPollingMessageListener(DynamicInterfaceBeanResolver dynamicInterfaceBeanResolver,
                                          List<PollingMessageExceptionHandler> pollingMessageExceptionHandlers) {
        this.dynamicInterfaceBeanResolver = dynamicInterfaceBeanResolver;
        this.pollingMessageExceptionHandlers = pollingMessageExceptionHandlers;
    }

    /**
     * 处理消息
     * @param message
     * @throws Exception
     */
    public void onMessage(Message<?> message) throws Exception {

        MessageHeaders messageHeaders = message.getHeaders();
        Object value = message.getPayload();
        if (value instanceof byte[]){
            value = new String((byte[]) value);
        }

        String messageKey = messageHeaders.get(PollingMessage.MESSAGE_KEY, String.class);
        String messageHandlerBeanName = messageHeaders.get(PollingMessage.MESSAGE_HANDLER, String.class);
        PollingMessageHandler messageHandler = dynamicInterfaceBeanResolver.getBean(
                messageHandlerBeanName,
                String.format("%s%s", lineToHump(messageKey), PollingMessageHandler.class.getSimpleName()),
                PollingMessageHandler.class);

        Class<? extends PollingMessage> paramType = getParamType(messageHandler);
        PollingMessage pollingMessage = JACKSON_OBJECT_MAPPER.readValue(String.valueOf(value), paramType);

        try {
            messageHandler.handleMessage(MessageBuilder.createMessage(pollingMessage, message.getHeaders()));
        } catch (Exception e) {
            if (!CollectionUtils.isEmpty(pollingMessageExceptionHandlers)){
                pollingMessageExceptionHandlers.stream().forEach(pollingMessageExceptionHandler -> {
                    pollingMessageExceptionHandler.onException(message, e);
                });
            }
        }
    }


    private Class<? extends PollingMessage> getParamType(PollingMessageHandler messageHandler) {
        return (Class<? extends PollingMessage>) ((ParameterizedType)messageHandler.getClass().getGenericInterfaces()[0]).getActualTypeArguments()[0];
    }


    /**
     * 下划线转驼峰
     *
     * @param str
     * @return
     */
    public static String lineToHump(String str) {
        Matcher matcher = LINE_PATTERN.matcher(str);
        StringBuffer sb = new StringBuffer();
        while (matcher.find()) {
            matcher.appendReplacement(sb, matcher.group(1).toUpperCase());
        }
        matcher.appendTail(sb);
        return sb.toString();
    }


    static class JacksonObjectMapper extends ObjectMapper {
        private static final String PATTERN_DATETIME = "yyyy-MM-dd HH:mm:ss";
        private static final String PATTERN_DATE = "yyyy-MM-dd";
        private static final String PATTERN_TIME = "HH:mm:ss";

        public JacksonObjectMapper() {
            this.setLocale(Locale.CHINA);
            this.setDateFormat(new SimpleDateFormat(PATTERN_DATETIME, Locale.CHINA));
            // 不序列化null值
            this.setSerializationInclusion(JsonInclude.Include.NON_NULL);
            // 单引号
            this.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
            this.configure(JsonParser.Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER, true);
            // 允许JSON字符串包含非引号控制字符（值小于32的ASCII字符，包含制表符和换行符）
            this.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);
            // 忽略json字符串中不识别的属性
            this.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
            // 忽略无法转换的对象
            this.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
            this.setTimeZone(TimeZone.getTimeZone(ZoneId.systemDefault()));
            this.findAndRegisterModules();
            this.registerModule(new JavaTimeModule());
        }

        static class JavaTimeModule extends SimpleModule {
            /**
             * java 8 时间格式化
             */
            private static final DateTimeFormatter DATETIME_FORMATTER = DateTimeFormatter.ofPattern(PATTERN_DATETIME);
            private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern(PATTERN_DATE);
            private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern(PATTERN_TIME);

            public JavaTimeModule() {
                super(PackageVersion.VERSION);
                this.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DATETIME_FORMATTER));
                this.addDeserializer(LocalDate.class, new LocalDateDeserializer(DATE_FORMATTER));
                this.addDeserializer(LocalTime.class, new LocalTimeDeserializer(TIME_FORMATTER));
                this.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DATETIME_FORMATTER));
                this.addSerializer(LocalDate.class, new LocalDateSerializer(DATE_FORMATTER));
                this.addSerializer(LocalTime.class, new LocalTimeSerializer(TIME_FORMATTER));
            }
        }
    }

}
